// 使用C++11的线程、互斥锁、条件变量实现生产者消费者代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono> 

using std::cout;
using std::endl;
using std::thread;
using std::mutex;
using std::unique_lock;
using std::condition_variable;
using std::queue;
using std::this_thread::sleep_for;

class TaskQueue
{
public:
    TaskQueue(size_t capacity = 10):_capacity(capacity){}

    void push(int data)
    {
        unique_lock<mutex> ul(_mutex);
        while(full()){_not_full.wait(ul);}
        _queue.push(data);
        _not_empty.notify_all();
    }

    int pop()
    {
        unique_lock<mutex> ul(_mutex);
        while(empty()){_not_empty.wait(ul);}
        int ret = _queue.front();
        _queue.pop();
        _not_full.notify_all();
        return ret;
    }
    
    bool empty(){return _queue.empty();}
    bool full(){return _queue.size() == _capacity;}
    mutex _m;
private:
    size_t _capacity;
    queue<int> _queue;
    mutex _mutex;
    condition_variable _not_empty;
    condition_variable _not_full;
};

class Producer
{
public:
    Producer() = default;
    ~Producer() = default;
    // 删除拷贝和赋值
    Producer(const Producer& rhs) = delete;
    Producer& operator=(const Producer& rhs) = delete;

    void prodece(TaskQueue& task_queue){
        ::srand(::time(nullptr));
        int count = 50;
        while(count--){
            int rand_data = ::rand() % 100;
            task_queue.push(rand_data);
            task_queue._m.lock();
            cout << "生产者生产了数据：" << rand_data << endl;
            task_queue._m.unlock();
        }
        cout << "生产者生产完毕" << endl;
    }
};

class Consumer
{
public:
    Consumer() = default;
    ~Consumer() = default;
    // 删除拷贝和赋值
    Consumer(const Consumer& rhs) = delete;
    Consumer& operator=(const Consumer& rhs) = delete;

    void consume(TaskQueue& task_queue){
        int count = 50;
        while(count--){
            int data = task_queue.pop();
            task_queue._m.lock();
            cout << "消费者消费了数据：" << data << endl;
            task_queue._m.unlock();
        }
        cout << "消费者消费完毕" << endl;
    }
};

int main()
{
    Producer p;

    Consumer c;

    TaskQueue task_queue(10);
    thread pt1(&Producer::prodece, &p, std::ref(task_queue));

    thread ct1(&Consumer::consume, &c, std::ref(task_queue));

    pt1.join();
    ct1.join();
    
    return 0;
}
